iT邦幫忙

2026 iThome 鐵人賽

DAY 6
0
Software Development

我是Java工程師,關於密碼學我想懂的不多系列 第 6

Day05 - 現代密碼學基礎 -- Base64與Hex Code

  • 分享至 

  • xImage
  •  

前言

你可能聽過有人把Base64當作資料庫密碼加密來用這樣的鬼故事,記住它只是改變字串表示方式改變字串表示方式改變字串表示方式,很重要所以說三次。

文字編碼 (Character Encoding)

我們必須先回到電腦最底層的現實:電腦只看得懂 0 與 1,根本不認識文字。

我們在 Java 程式中寫下的任何字串(例如 "Hello""哈囉"),都必須先透過 文字編碼 (Character Encoding) 翻譯成一串數字(byte[]),電腦才能儲存或處理。

1. 常見的文字編碼規範

  • ASCII:最早期、最簡單的標準。用 7 個 bit(1 Byte)表示 128 個英文大小寫字母、數字與常用符號(A 的 ASCII 碼是 65 / 0x41)。
  • Unicode (萬國碼):為了讓全世界的語言(中文、日文、Emoji 等)都能有統一的編號而誕生的巨型對照表,記住它是「字碼標準」。
  • UTF-8:它是可變長度編碼,英文字母只佔 1 個 Byte(完全相容 ASCII),中文通常佔 3 個 Bytes,Emoji 則佔 4 個 Bytes。記住它是 Unicode 的一種實作(其他實作還有 UTF-16、UTF-32 等)。

2. Java 工程師一定要懂的事

Java 的 String 是 Unicode 字元序列,密碼學 API 多半接收 byte[]。從字串轉成位元組必須明確指定 UTF-8,否則不同平台預設 charset 可能產生不同結果。

// 錯誤作法:直接調用getBytes()會使用作業系統預設的編碼 Linux 上可能是 UTF-8,但在 Windows 上可能是 CP950 (Big5)
// 這會導致相同的程式碼在不同作業系統跑出完全不同的 byte[]!
byte[] dangerousBytes = "哈囉".getBytes();

// 正確做法:永遠明確指定 StandardCharsets.UTF_8
byte[] safeBytes = "哈囉".getBytes(StandardCharsets.UTF_8);

密碼學最常用的兩大編碼

1. 十六進位編碼 (Hexadecimal / Hex)

  • 機制:把 1 個 Byte(8 bits)拆成兩半,每 4 個 bits 用 1 個十六進位字元(0-9, a-f)表示。
  • 長度變更:原本 1 個 Byte 會變成 2 個字元(資料量變為 200%)。
  • 常用場景:印出 SHA-256 雜湊值(例如 32 Bytes 的 SHA-256 會顯示為 64 個字元的 Hex 字串)、排查 API 簽章與金鑰內容。

https://ithelp.ithome.com.tw/upload/images/20260920/20128084nPh2VaanBw.png

2. Base64 編碼

  • 機制:把 3 個 Bytes(24 bits)切成 4 個區塊(每區塊 6 bits),對應到 64 個絕對安全的 ASCII 可列印字元(A-Z, a-z, 0-9, +, /)。不足 3 Bytes 時尾端補 =
  • 長度變更:每 3 個 Bytes 轉成 4 個字元(資料量僅膨脹約 133%,傳輸效率大幅優於 Hex)。
  • 變體 (Base64URL):將 + 換成 -/ 換成 _,並去掉尾端的 =,避免在 URL 中被網頁伺服器誤判。
  • 常用場景:PEM 憑證/金鑰檔案(-----BEGIN PUBLIC KEY----- 後面的內容)、JWT Token、API 加密欄位傳輸。
String original = "密碼";
byte[] utf8 = original.getBytes(StandardCharsets.UTF_8);

String base64 = Base64.getEncoder().encodeToString(utf8);
String base64url = Base64.getUrlEncoder().encodeToString(utf8);
String hex = HexFormat.of().withUpperCase().withDelimiter(" ").formatHex(utf8);
String restored = new String(
        Base64.getDecoder().decode(base64), StandardCharsets.UTF_8);

System.out.println("Base64: " + base64);//5a+G56K8
System.out.println("Base64url: " + base64url);//5a-G56K8
System.out.println("Hex: " + hex);//E5 AF 86 E7 A2 BC
System.out.println("Restored equals original: " + restored.equals(original));

為什麼密碼學還需要 Base64 與 Hex?

文字經過 UTF-8 轉成 byte[] 後,就能交給 AES 或 RSA 進行加密運算。

但這裡會有一個新問題:加解密與雜湊運算出來的結果,是完全隨機的二進位陣列 (byte[])

這意味著每一顆 Byte 都有可能出現 x00 ~ xFF(256 種組合)中的任意數值,包含大量的控制字元(如 0x00 Null、0x0A 換行)或 UTF-8 無法識別的非法片段。

如果直接將密文 byte[] 硬轉回 UTF-8 字串(例如 new String(cipherBytes, StandardCharsets.UTF_8)),UTF-8 引擎會因為無法解析而直接塗改毀損資料(替換成 ``),導致解密必定失敗!

為了將這些亂七八糟的二進位密文安全地放入 HTTP Header、JSON、URL 或資料庫中,我們需要第二層轉碼——將二進位編碼為安全的 ASCII 純文字


上一篇
Day04 - 現代密碼學
下一篇
Day06 - 現代密碼學基礎 -- XOR 運算與可逆性
系列文
我是Java工程師,關於密碼學我想懂的不多8
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言